﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;

namespace Marmat.Forms
{

    /// <summary>
    /// 表示一个由可移动条组成的控件，该可移动条将容器的显示区域分成两个大小可调的面板。
    /// </summary>
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(System.Windows.Forms.SplitContainer))]
    public partial class SplitContainer : System.Windows.Forms.SplitContainer
    {
        private ControlState _mouseState;
        private int _minSize;
        private HistTest _histTest;

        [DebuggerNonUserCode()]
        public SplitContainer() : base()
        {
            SetStyle(ControlStyles.ResizeRedraw | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
            // 组件设计器需要此调用。
            this.InitializeComponent();
            SplitterDistance = this.LastDistance;
            this.DoubleBuffered = true;
        }

        [Browsable(false)]
        public int LastDistance
        {
            get
            {
                return this.m_lastDistance;
            }

            set
            {
                this.m_lastDistance = value;
            }
        }

        private int m_lastDistance;


        /// <summary>
        /// 分割器边框外部的渐变色颜色值。
        /// </summary>
        /// <returns></returns>
        [DefaultValue(typeof(Color), "Transparent")]
        [Category("外观")]
        [Description("分割器边框外部的渐变色颜色值。")]
        public Color SplitterBorderExternalColor { get; set; } = Color.Transparent;

        /// <summary>
        /// 分割器边框内部的渐变色颜色值。
        /// </summary>
        /// <returns></returns>
        [DefaultValue(typeof(Color), "Transparent")]
        [Category("外观")]
        [Description("分割器边框内部的渐变色颜色值。")]
        public Color SplitterBorderInteriorColor { get; set; } = Color.Transparent;

        /// <summary>
        /// 分割器边框内部的前景色。
        /// </summary>
        /// <returns></returns>
        [DefaultValue(typeof(Color), "Black")]
        [Category("外观")]
        [Description("分割器边框内部的前景色。")]
        public Color SplitterBorderForeColor { get; set; } = Color.Black;

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public SplitContainerRenderer Renderer
        {
            get
            {
                return this.m_Renderer;
            }

            set
            {
                this.m_Renderer = value;
            }
        }

        private SplitContainerRenderer m_Renderer;

        public event EventHandler CollapseClick;

        [DefaultValue(typeof(CollapsePanel), "None")]
        [Category("外观")]
        [Description("获取或设置鼠标点击时所折叠的方向。")]
        public CollapsePanel CollapsePanel
        {
            get
            {
                return this.m_collapsePanel;
            }

            set
            {
                if (this.m_collapsePanel != value)
                {
                    this.Expand();
                    this.m_collapsePanel = value;
                    this.Invalidate();
                }
            }
        }

        private CollapsePanel m_collapsePanel = CollapsePanel.None;

        protected virtual int DefaultCollapseWidth
        {
            get
            {
                return 80;
            }
        }

        protected virtual int DefaultArrowWidth
        {
            get
            {
                return 16;
            }
        }

        protected Rectangle CollapseRect
        {
            get
            {
                if (this.m_collapsePanel == CollapsePanel.None)
                {
                    return Rectangle.Empty;
                }

                var rect = SplitterRectangle;
                if (Orientation == Orientation.Horizontal)
                {
                    rect.X = (Width - this.DefaultCollapseWidth) / 2;
                    rect.Width = this.DefaultCollapseWidth;
                }
                else
                {
                    rect.Y = (Height - this.DefaultCollapseWidth) / 2;
                    rect.Height = this.DefaultCollapseWidth;
                }

                return rect;
            }
        }

        [Browsable(false)]
        [DefaultValue(typeof(CollapsePanel), "Expanded")]
        public SpliterPanelState SpliterPanelState
        {
            get
            {
                return this.m_SpliterPanelState;
            }

            set
            {
                if (this.m_SpliterPanelState != value)
                {
                    WindowsAPI.LockWindowUpdate((int)this.Handle);
                    switch (value)
                    {
                        case SpliterPanelState.Expanded:
                            {
                                this.Expand();
                                break;
                            }

                        case SpliterPanelState.Collapsed:
                            {
                                this.Collapse();
                                break;
                            }
                    }

                    this.m_SpliterPanelState = value;
                    WindowsAPI.LockWindowUpdate((int)IntPtr.Zero);
                }
            }
        }

        private SpliterPanelState m_SpliterPanelState = SpliterPanelState.Expanded;

        internal ControlState MouseState
        {
            get
            {
                return this._mouseState;
            }

            set
            {
                if (this._mouseState != value)
                {
                    this._mouseState = value;
                    Invalidate(this.CollapseRect);
                }
            }
        }

        /// <summary>
        /// 收缩
        /// </summary>
        /// <remarks></remarks>
        public void Collapse()
        {
            if (this.m_collapsePanel != CollapsePanel.None && this.m_SpliterPanelState == SpliterPanelState.Expanded)
            {
                this.LastDistance = SplitterDistance;
                if (this.m_collapsePanel == CollapsePanel.Panel1)
                {
                    this._minSize = Panel1MinSize;
                    Panel1MinSize = 0;
                    SplitterDistance = 0;
                }
                else
                {
                    int width = Orientation == Orientation.Horizontal ? Height : Width;
                    this._minSize = Panel2MinSize;
                    Panel2MinSize = 0;
                    SplitterDistance = width - SplitterWidth - Padding.Vertical;
                }

                Invalidate(SplitterRectangle);
            }
        }

        /// <summary>
        /// 展开
        /// </summary>
        /// <remarks></remarks>
        public void Expand()
        {
            if (this.m_collapsePanel != CollapsePanel.None && this.m_SpliterPanelState == SpliterPanelState.Collapsed)
            {
                if (this.m_collapsePanel == CollapsePanel.Panel1)
                {
                    Panel1MinSize = this._minSize;
                }
                else
                {
                    Panel2MinSize = this._minSize;
                }

                SplitterDistance = this.LastDistance;
                Invalidate(SplitterRectangle);
            }
        }

        protected virtual void OnCollapseClick(EventArgs e)
        {
            if (this.m_SpliterPanelState == SpliterPanelState.Collapsed)
            {
                this.SpliterPanelState = SpliterPanelState.Expanded;
            }
            else
            {
                this.SpliterPanelState = SpliterPanelState.Collapsed;
            }
            // Dim handler As EventHandler = TryCast(MyBase.Events(EventCollapseClick), EventHandler)
            // Dim handler As EventHandler = TryCast(MyBase.Events(EventCollapseClick), EventHandler)
            // handler()
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (Panel1Collapsed || Panel2Collapsed || this.CollapsePanel == CollapsePanel.None)
                return;
            SplitContainerRenderer Renderer = this.Renderer ?? new SplitContainerRenderer(this);
            using (var Bmp = new Bitmap(this.SplitterRectangle.Width, this.SplitterRectangle.Height))
            {
                using (var G_Bmp = Graphics.FromImage(Bmp))
                {
                    Renderer.OnRenderSplitterBackground(new PaintEventArgs(G_Bmp, new Rectangle(0, 0, Bmp.Width, Bmp.Height)));
                    e.Graphics.DrawImage(Bmp, this.SplitterRectangle);
                }
            }

            if (this.m_collapsePanel == CollapsePanel.None)
                return;
            var Rect = this.CollapseRect;
            using (var Bmp = new Bitmap(Rect.Width, Rect.Height))
            {
                using (var G_Bmp = Graphics.FromImage(Bmp))
                {
                    Renderer.OnRenderSplitterModalBackground(new PaintEventArgs(G_Bmp, new Rectangle(0, 0, Rect.Width, Rect.Height)));
                    e.Graphics.DrawImage(Bmp, Rect);
                }
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            // 如果鼠标的左键没有按下，重置HistTest
            if (e.Button != MouseButtons.Left)
            {
                this._histTest = HistTest.None;
            }

            var collapseRect__1 = this.CollapseRect;
            var mousePoint = e.Location;

            // 鼠标在Button矩形里，并且不是在拖动
            if (collapseRect__1.Contains(mousePoint) && this._histTest != HistTest.Spliter)
            {
                Capture = false;
                this.SetCursor(Cursors.Hand);
                this.MouseState = ControlState.Hover;
                return;
            }
            // 鼠标在分隔栏矩形里
            else if (SplitterRectangle.Contains(mousePoint))
            {
                this.MouseState = ControlState.Normal;

                // 如果已经在按钮按下了鼠标或者已经收缩，就不允许拖动了
                if (this._histTest == HistTest.Button || this.m_collapsePanel != CollapsePanel.None && this.m_SpliterPanelState == SpliterPanelState.Collapsed)
                {
                    Capture = false;
                    this.Cursor = null;
                    return;
                }

                // 鼠标没有按下，设置Split光标
                if (this._histTest == HistTest.None && !IsSplitterFixed)
                {
                    if (Orientation == Orientation.Horizontal)
                    {
                        this.SetCursor(Cursors.HSplit);
                    }
                    else
                    {
                        this.SetCursor(Cursors.VSplit);
                    }

                    return;
                }
            }

            this.MouseState = ControlState.Normal;

            // 正在拖动分隔栏
            if (this._histTest == HistTest.Spliter && !IsSplitterFixed)
            {
                if (Orientation == Orientation.Horizontal)
                {
                    this.SetCursor(Cursors.HSplit);
                }
                else
                {
                    this.SetCursor(Cursors.VSplit);
                }

                base.OnMouseMove(e);
                return;
            }

            // Me.Cursor = System.Windows.Forms.Cursors.Default
            this.Cursor = null;
            base.OnMouseMove(e);
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            // Me.Cursor = System.Windows.Forms.Cursors.Default
            this.Cursor = null;
            this.MouseState = ControlState.Normal;
            base.OnMouseLeave(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            var collapseRect__1 = this.CollapseRect;
            var mousePoint = e.Location;
            if (collapseRect__1.Contains(mousePoint) || this.m_collapsePanel != CollapsePanel.None && this.m_SpliterPanelState == SpliterPanelState.Collapsed)
            {
                this._histTest = HistTest.Button;
                return;
            }

            if (SplitterRectangle.Contains(mousePoint))
            {
                this._histTest = HistTest.Spliter;
            }

            base.OnMouseDown(e);
        }

        protected new void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            Invalidate(SplitterRectangle);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            Invalidate(SplitterRectangle);
            var collapseRect__1 = this.CollapseRect;
            var mousePoint = e.Location;
            if (this._histTest == HistTest.Button && e.Button == MouseButtons.Left && collapseRect__1.Contains(mousePoint))
            {
                this.OnCollapseClick(EventArgs.Empty);
            }

            this._histTest = HistTest.None;
        }

        private void SetCursor(Cursor cursor)
        {
            if (!ReferenceEquals(this.Cursor, cursor))
            {
                this.Cursor = cursor;
            }
        }

        internal void CalculateRect(Rectangle collapseRect, ref Rectangle arrowRect, ref Rectangle topLeftRect, ref Rectangle bottomRightRect)
        {
            int width;
            if (Orientation == Orientation.Horizontal)
            {
                width = (collapseRect.Width - this.DefaultArrowWidth) / 2;
                arrowRect = new Rectangle(collapseRect.X + width, collapseRect.Y, this.DefaultArrowWidth, collapseRect.Height);
                topLeftRect = new Rectangle(collapseRect.X, collapseRect.Y + 1, width, collapseRect.Height - 2);
                bottomRightRect = new Rectangle(arrowRect.Right, collapseRect.Y + 1, width, collapseRect.Height - 2);
            }
            else
            {
                width = (collapseRect.Height - this.DefaultArrowWidth) / 2;
                arrowRect = new Rectangle(collapseRect.X, collapseRect.Y + width, collapseRect.Width, this.DefaultArrowWidth);
                topLeftRect = new Rectangle(collapseRect.X + 1, collapseRect.Y, collapseRect.Width - 2, width);
                bottomRightRect = new Rectangle(collapseRect.X + 1, arrowRect.Bottom, collapseRect.Width - 2, width);
            }
        }

        private enum HistTest
        {
            None,
            Button,
            Spliter
        }
    }
}